package com.demo.webapp.ui.menu.displayer;

import java.io.IOException;
import java.util.Locale;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.StrutsStatics;
import org.apache.struts2.dispatcher.Dispatcher;
import org.apache.struts2.dispatcher.ng.listener.StrutsListener;
import org.apache.struts2.views.freemarker.FreemarkerManager;
import org.apache.struts2.views.freemarker.ScopesHashModel;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;

import com.demo.webapp.ui.ComponentBase;
import com.demo.webapp.ui.access.DisplayerAuthority;
import com.demo.webapp.ui.access.PermissionsAdapter;
import com.demo.webapp.ui.displayer.DisplayerMapping;
import com.demo.webapp.ui.displayer.MessageResourcesDisplayer;
import com.demo.webapp.ui.menu.MenuComponent;
import com.demo.webapp.ui.menu.displayer.OldFreemarkerMenuDisplayer;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.LocaleProvider;

import freemarker.template.Configuration;
import freemarker.template.ObjectWrapper;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateModel;

/**
 * FreemarkerMenuDisplayer
 * 使用freemarker作为Menu的渲染对象;<br>
 * 该对象依赖于Struts2。使用FreemarkerManager对象进行管理<br>
 * 请参照Struts2的Freemarker配置说明<br>
 * FreemarkerMenuDisplayer提供的参数：
 * <ol>
 *	<li>ognl : {@link org.apache.struts2.views.jsp.ui.OgnlTool}</li>
 *	<li>action</li>
 *	<li>request</li>
 *	<li>session</li>
 *	<li>application</li>
 *	<li>menu</li>
 *	<li>displayer</li>
 *	<li>menuId</li>
 * </ol>
 * <code>MenuDisplayer</code>
 * <p>
 * 例如：
 * </p><pre>   
 *&lt;menu:menuDisplayer name="Freemarker" config="/WEB-INF/menu/cssVerticalMenu.ftl" permissions="rolesAdapter"&gt;
 *	&lt;menu:menu name="AdminMenu"/&gt;
 *&lt;/menu:menuDisplayer&gt;</pre>
 *<p>
 *<ul><li>name<br>
 *	映射为使用menu-config.xml中配置的Displayer名称；该配置freemarker渲染该menu内容</li>
 *	<li>config<br>
 *	使用哪种模板文件渲染Menu『config的基础路径为classpath.』</li>
 *</ul>
 *</p> 
 *      
 * @author ryuu.kk
 * @version 1.0
 */
public class OldFreemarkerMenuDisplayer extends MessageResourcesDisplayer<MenuComponent> implements DisplayerAuthority<Authentication> {

	/**
	 * 日志
	 */
    protected static final Log log = LogFactory.getLog(OldFreemarkerMenuDisplayer.class);
    /**
     * 默认导航分隔符
     */
    protected static String BREAD_CRUMB_DELIMITER = " > ";
    /**
     * Struts2的freemarker配置管理器
     */
    private static FreemarkerManager freemarkerManager;
    /**
     * freemarker配置
     */
    private Configuration config;
    /**
     * PageContext
     */
    private PageContext pageContext = null;
    /**
     * objectWrapper
     */
    private ObjectWrapper wrapper;

    
    protected PermissionsAdapter<Authentication> permissionsAdapter = null;
    
	@Override
	public String getType() {
		//return Type.menu.toString();
		return null;
	}

	/**
	 * 使用默认的message本地化消息有一个线程同步问题
	 * 当因为displayer使用对象Map存储,则每个用户共享Map的displayer
	 * 当用户设置setLocale时,将会影响其他用户locale设置
	 * 这里使用spring的本地local做为用户选择的locale
	 * @see com.demo.webapp.filter.LocaleFilter
	 * @see org.springframework.context.i18n.LocaleContextHolder
	 */
	@Override
	public String beforeMessage(String code, Object[] args, Locale locale) {
		//使用spring提供的资源服务
//		if (messageSource != null) {
//			//使用当前用户Locale作为默认Locale
//			Locale currentlocale = LocaleContextHolder.getLocale();
//			return messageSource.getMessage(code, args, code, currentlocale == null ? locale : currentlocale);
//		}
		if (log.isDebugEnabled()) {
			log.debug("Look at " + code + " messages.");
		}
		return null;
	}
	
    /**
     * 对FreemarkerMenuWithStruts2Displayer进行配置
     * @param context ServletContext
     */
    protected static void initialize(ServletContext context) {
    	if (freemarkerManager != null) {
    		return;
    	}
        Dispatcher dispatcher = (Dispatcher) context.getAttribute(StrutsStatics.SERVLET_DISPATCHER);
        if (dispatcher == null) {
            throw new IllegalStateException("Unable to find the Dispatcher in the Servlet Context. Is '" + StrutsListener.class.getName() + "' missing in web.xml?");
        }
        freemarkerManager = dispatcher.getContainer().getInstance(FreemarkerManager.class);
    }
    
    /**
     * 调用初始化;
     * 进行menu的渲染。参照{@link net.sf.navigator.taglib.UseMenuDisplayerTag}
     * @see net.sf.navigator.taglib.UseMenuDisplayerTag
     * @param pageContext PageContext
     * @param mapping MenuDisplayerMapping
     */
    public void init(PageContext pageContext, DisplayerMapping mapping) {
        super.init(pageContext, mapping);
        this.pageContext = pageContext;
        /*
        if (freemarkerManager == null) {
        	//dispatcher.getConfigurationManager();
            //Dispatcher dispatcher = (Dispatcher) getServletContext().getAttribute(StrutsStatics.SERVLET_DISPATCHER);
        	InitOperations init = new InitOperations();
            Dispatcher dispatcher = init.findDispatcherOnThread();
            if (dispatcher == null) {
                throw new IllegalStateException("Unable to find the Dispatcher in the Servlet Context. Is '" + StrutsListener.class.getName() + "' missing in web.xml?");
            }
            freemarkerManager = dispatcher.getContainer().getInstance(FreemarkerManager.class);
        }
        */
        initialize(pageContext.getServletContext());
        config = freemarkerManager.getConfiguration(pageContext.getServletContext());
        wrapper = config.getObjectWrapper();
    }
    /**
     * 渲染展示
     * 
     * @param menu Menu容器对象,该对象是一个链式对象。实现多层树形Menu
     */
	@Override
	public void display(MenuComponent menu) throws JspException, IOException {
		//Authentication config from spring security
		Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
		//不需要权限认证&用户有访问许可时,可以访问
		boolean permise = getPermissionsAdapter() == null? true : getPermissionsAdapter().hasPermission(menu, authentication);
        if (permise) {
            try {
				displayComponents(menu);
			} catch (TemplateException e) {
				e.printStackTrace();
				log.error(e.getMessage());
			}
        }
	}
	
	@Override
	public void end(PageContext pageContext) throws JspException {
		//
	}
	
	protected void displayComponents(ComponentBase menu) throws JspException, IOException, TemplateException {
		HttpServletRequest request = (HttpServletRequest) pageContext.getRequest();
		HttpServletResponse response = (HttpServletResponse) pageContext.getResponse();
		ActionContext ctx = ServletActionContext.getActionContext(request);
		//get template in user page.
		Template template = config.getTemplate(getConfig(), getLocale((Action) ctx.getActionInvocation().getAction()));
		
		Object attrContentType = template.getCustomAttribute("content_type");
        if (attrContentType != null) {
            response.setContentType(attrContentType.toString());
        } else {
            if (freemarkerManager.getNoCharsetInContentType()) {
                response.setContentType(freemarkerManager.getContentType() + "; charset=" + template.getEncoding());
            } else {
                response.setContentType(freemarkerManager.getContentType());
            }
        }
        ScopesHashModel model = (ScopesHashModel) request.getAttribute(FreemarkerManager.ATTR_TEMPLATE_MODEL);
        ServletContext servletContext = request.getSession().getServletContext();
        if (model == null) {
		    model = freemarkerManager.buildTemplateModel(ctx.getValueStack(), ctx.getActionInvocation().getAction(), servletContext, request, response, wrapper);
		}
        if (preTemplateProcess(pageContext, menu, template, model)) {
            try {
                // Process the template
                template.process(model, pageContext.getOut());
            } finally {
                //postTemplateProcess(request, response, template, model);
            }
        }
	}
	/**
	 * 模板的前置处理方法
	 * 该方法将Template提供参数
	 * @param pageContext PageContext
	 * @param menu MenuComponent
	 * @param template FreemarkerTemplate
	 * @param templateModel TemplateModel
	 * @return boolean
	 */
	protected boolean preTemplateProcess(PageContext pageContext, ComponentBase menu, Template template, TemplateModel templateModel) {
		boolean result = true;

		ScopesHashModel hash = (ScopesHashModel) templateModel;
		//add something.
		hash.put("menu", menu);
        if (pageContext.getAttribute("menuId") != null) {
        	hash.put("menuId", pageContext.getAttribute("menuId"));
        }
        hash.put("displayer", this);
		return result;
	}
	
	/**
	 * 使用Action的Locale作为优先Locale
	 * @param action Action动作
	 * @return Locale
	 */
	protected Locale getLocale(Action action) {
		if (action instanceof LocaleProvider) {
            return ((LocaleProvider) action).getLocale();
        } else {
            return super.getLocale();
        }
	}

	@Override
	public boolean hasPermission(ComponentBase model) {
		Authentication authentication = getAuthentication();
		return permissionsAdapter == null ? true : permissionsAdapter.hasPermission(model, authentication);
	}

	@Override
	public PermissionsAdapter<Authentication> getPermissionsAdapter() {
		return this.permissionsAdapter;
	}

	@Override
	public void setPermissionsAdapter(PermissionsAdapter<Authentication> permissionsAdapter) {
		this.permissionsAdapter = permissionsAdapter;
	}

	/**
	 * 返回用户认证
	 * @return 用户认证
	 */
	private Authentication getAuthentication() {
		return SecurityContextHolder.getContext().getAuthentication();
	}
}
